package tests;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.studerb.model.Header;
import com.studerb.model.NewsGroup;
import com.studerb.service.interfaces.IHeaderService;
import com.studerb.service.interfaces.INewsGroupService;

public class TestRegexSubject {
	private static boolean failed = false;
	private static Logger logger = Logger.getLogger(TestRegexSubject.class);
	private static Pattern subjectPattern = Pattern.compile("^(.*)([\\(\\[](\\d+)\\/(\\d+)[\\)\\]])(.*)");
	private static Map<String, Thread> threads = new HashMap<String, Thread>();
	List<Header> singlePart = new LinkedList<Header>();
	private static List<Header> errorHeaders = new ArrayList<Header>();
	private static final String NG_Name = "alt.binaries.3d";
	private static INewsGroupService newsGroupService;
	private static IHeaderService headerService;
	private static ClassPathXmlApplicationContext springAppContext;
	private static NewsGroup newsGroup = null;

	private static void addSubject(Header header, Matcher matcher) {
		String bareSubject = header.getSubject().replace(matcher.group(2), "") + matcher.group(5);
		int current = Integer.parseInt(matcher.group(3));
		int total = Integer.parseInt(matcher.group(4));
		if (!threads.containsKey(bareSubject)) {
			Thread thread = new Thread(bareSubject, total);
			threads.put(bareSubject, thread);
		}
		Thread t = threads.get(bareSubject);
		t.addHeader(header, current);
	}

	private static void createThreads(List<Header> headers) {
		List<Header> singlePart = new ArrayList<Header>();
		for (Header header : headers) {
			String subject = header.getSubject();
			Matcher matcher = subjectPattern.matcher(subject);
			if (matcher.find()) {
				addSubject(header, matcher);
			} else {
				singlePart.add(header);
			}
		}
	}

	public static void main(String args[]) {
		setUpSpring();
		try {
			setNewsGroup(NG_Name);
			// List<Header> headers = headerService.getHeaders(newsGroup);
			List<Header> headers = null;
			createThreads(headers);
			printStats();
		} catch (Exception e) {
			failed = true;
			e.printStackTrace();
		} finally {
			springAppContext.close();
		}

		System.exit(failed ? 1 : 0);
	}

	private static void printStats() {
		logger.debug("Total Threads: " + threads.size());
		Set<String> subjects = threads.keySet();
		for (String s : subjects) {
			Thread t = threads.get(s);
			if (!t.isComplete()) {
				logger.debug(s + " " + t.getCount() + " / " + t.isComplete() + " Filled: " + t.getFilledCount());
			}
		}

		logger.debug("\n\n--------------Errors--------------");
		for (Header h : errorHeaders) {
			logger.warn(h);
		}
	}

	private static void setNewsGroup(String groupName) {
		List<NewsGroup> newsGroups = newsGroupService.getNewsGroups();
		for (NewsGroup ng : newsGroups) {
			if (ng.getGroupName().equals(NG_Name)) {
				newsGroup = ng;
				break;
			}
		}
	}

	private static void setUpSpring() {
		logger.debug("Starting Spring Application Context");
		springAppContext = new ClassPathXmlApplicationContext(new String[] { "applicationContext.xml" });
		springAppContext.registerShutdownHook();
		newsGroupService = (INewsGroupService) springAppContext.getBean("newsGroupService");
		headerService = (IHeaderService) springAppContext.getBean("headerService");
	}

	static class Thread {
		private final String bareSubject;
		private final int count;
		private final Header[] headers;
		private boolean hasZeroIndex = false;

		public Thread(String bareSubject, int count) {
			this.bareSubject = bareSubject;
			this.count = count;
			headers = new Header[count + 1];

		}

		public void addHeader(Header header, int current) {
			if (current > headers.length) {
				throw new RuntimeException(String.format(header.getSubject() + "  adding %d to thread with max length of %d", current, count));
			}
			if (current == 0) {
				headers[0] = header;
				hasZeroIndex = true;
				return;
			}

			if (headers[current] != null) {
				errorHeaders.add(headers[current]);
				errorHeaders.add(header);
				return;
				// throw new RuntimeException(
				// "Adding new header to thread where already positioned at: " +
				// current);
			}

			headers[current] = header;
		}

		public String getBareSubject() {
			return bareSubject;
		}

		public int getCount() {
			return count;
		}

		public int getFilledCount() {
			int filled = 0;
			for (int i = 1; i < headers.length; ++i) {
				if (headers[i] != null) {
					filled++;
				}
			}
			return filled;
		}

		public Header[] getHeaders() {
			return headers;
		}

		public boolean isComplete() {
			if (hasZeroIndex && headers[0] == null) {
				return false;
			}
			for (int i = 1; i <= count; ++i) {
				if (headers[i] == null) {
					return false;
				}
			}
			return true;
		}
	}
}